package com.ezlcp.user.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ezlcp.commons.base.db.BaseDao;
import com.ezlcp.commons.base.db.BaseService;
import com.ezlcp.commons.base.entity.IUser;
import com.ezlcp.commons.base.entity.JsonResult;
import com.ezlcp.commons.constant.Constants;
import com.ezlcp.commons.constant.StatusEnum;
import com.ezlcp.commons.service.impl.SuperServiceImpl;
import com.ezlcp.commons.tool.IdGenerator;
import com.ezlcp.commons.tool.StringUtils;
import com.ezlcp.commons.utils.ContextUtil;
import com.ezlcp.user.entity.*;
import com.ezlcp.user.enums.MenuTypeEnum;
import com.ezlcp.user.mapper.*;
import jakarta.annotation.Resource;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.ezlcp.commons.constant.Constants.COL_MENU_ID;
import static com.ezlcp.commons.constant.Constants.COL_TENANT_ID;


/**
 * [系统菜单表]业务服务类
 */
@Service
public class MenuServiceImpl extends SuperServiceImpl<MenuMapper, Menu> implements BaseService<Menu> {
    public static final String COL_MENU_TYPE = "menu_type";

    @Autowired
    private MenuMapper menuMapper;
    @Resource
    private GroupMapper groupMapper;
    @Resource
    private PageElementMapper pageElementMapper;
    @Resource
    private GroupMenuMapper groupMenuMapper;
    @Resource
    private TenantMenuMapper tenantMenuMapper;

    /**
     * @param tenantId 公司ID
     * @return java.util.List<com.ezlcp.user.entity.Menu>
     * @description: 公司用户获取公司所有菜单
     * @author Elwin ZHANG
     * @date 2022/5/11 10:23
     */
    public List<TenantMenu> getTenantAllMenus(@NonNull String tenantId) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq(Constants.COL_TENANT_ID, tenantId);
        wrapper.eq(Constants.COL_STATUS, StatusEnum.enable.getValue());
        wrapper.orderByAsc("parent_id","sort");
        return tenantMenuMapper.selectList(wrapper);
    }

    /***
     * @description: 获取平台的所有菜单
     * @author Elwin ZHANG
     * @date 2022/5/11 13:41
     */
    public List<TenantMenu> getPlatformMenus() {
        QueryWrapper<TenantMenu> wrapper = new QueryWrapper<>();
        wrapper.lt(Constants.COL_STATUS, StatusEnum.deleted.getValue());
        wrapper.and(query -> query.eq(Constants.COL_TENANT_ID, "").or().isNull(Constants.COL_TENANT_ID));
        wrapper.orderByAsc("parent_id","sort");
        return tenantMenuMapper.selectList(wrapper);
    }

    /***
     * @description 查询当前菜单可选择的父节点(排除当前节点 ， 只能选择类型为目录的菜单)
     * @param pkId 当前菜单ID
     * @return java.util.List<com.ezlcp.user.entity.TenantMenu>
     * @author Elwin ZHANG
     * @date 2023/4/3 11:20
     */
    public List<TenantMenu> selectParent(String pkId) {
        QueryWrapper<TenantMenu> wrapper = new QueryWrapper<>();
        wrapper.lt(Constants.COL_STATUS, StatusEnum.deleted.getValue());
        wrapper.eq(COL_MENU_TYPE, MenuTypeEnum.Directory.getValue());
        String tenantId = ContextUtil.getCurrentTenantId();
        if (StringUtils.isEmpty(tenantId)) {
            wrapper.and(query -> query.eq(Constants.COL_TENANT_ID, "").or().isNull(Constants.COL_TENANT_ID));
        } else {
            wrapper.eq(Constants.COL_TENANT_ID, tenantId);
        }
        if (StringUtils.isNotEmpty(pkId)) {
            wrapper.ne("id", pkId);
        }
        return tenantMenuMapper.selectList(wrapper);
    }

    /***
     * @description 初始化租户菜单
     * @param tenantId 租户ID
     * @author Elwin ZHANG
     * @date 2023/3/27 17:24
     */
    public void initTenantMenus(String tenantId) {
        menuMapper.initTenantMenu1(tenantId);
        menuMapper.initTenantMenu2(tenantId);
    }

    /***
     * @description: 获取平台用户的授权菜单
     * @param userId 用户ID
     * @return java.util.List<com.ezlcp.user.entity.Menu>
     * @author Elwin ZHANG
     * @date 2022/5/11 13:39
     */
    public List<TenantMenu> getPlatUserMenus(String userId) {
        return menuMapper.getPlatUserMenus(userId);
    }

    /**
     * @param userId   用户ID
     * @param tenantId 公司ID
     * @return java.util.List<com.ezlcp.user.entity.Menu>
     * @description: 获取公司用户的授权菜单
     * @author Elwin ZHANG
     * @date 2022/6/9 9:53
     */
    public List<TenantMenu> getCommonUserMenus(String userId, String tenantId) {
        List<TenantMenu> menus = menuMapper.getCommonUserMenus(userId, tenantId);
        return menus;
    }

    /**
     * @return java.util.List<java.util.HashMap>
     * @description: 查询当前用户全部的按钮权限
     * @author Elwin ZHANG
     * @date 2023/5/12 14:30
     */
    public Collection<PageElement> getCurUserPageAuth() {
        IUser user = ContextUtil.getCurrentUser();
        //全部的界面元素权限
        List<PageElement> allAuths = pageElementMapper.selectList(null);
        //超级管理拥有全部权限
        if (user.isSuperAdmin()) {
            return allAuths;
        }
        //公司管理员拥有除公司管理外的全部权限
        if(user.isAdmin()){
            allAuths.remove(2);
            allAuths.remove(1);
            allAuths.remove(0);
            return allAuths;
        }
        //其他用户要查授权记录
        List<GroupMenu> pageAuths = menuMapper.getUserPageAuth(user.getUserId(), null);
        //没有授权，或菜单下没有控制到按钮
        if (pageAuths == null || pageAuths.size() == 0 ) {
            return new ArrayList<>();
        }
        //当前权限
        HashMap<String,PageElement> curAuths = new HashMap<>();
        //循环处理权限
        for (GroupMenu pageAuth : pageAuths) {
            if (pageAuth.getHasElement() == 0) {
                continue;
            }
            String strElements = pageAuth.getElements();
            if (StringUtils.isEmpty(strElements) || "[]".equals(strElements)) {
                continue;
            }
            collectElements(pageAuth.getMenuId(), strElements, allAuths, curAuths);
        }
        return curAuths.values();
    }

    /***
     * @description 根据当前权限信息，将权限记录从allAuths移动到curAuths
     * @param menuId 菜单ID
     * @param strElements 授权信息
     * @param allAuths 初始为全部按钮权限
     * @param curAuths 当前用户的按钮权限
     * @author Elwin ZHANG
     * @date 2023/5/12 17:05
     */
    private void collectElements(String menuId, String strElements, List<PageElement> allAuths, HashMap<String,PageElement> curAuths) {
        if (allAuths.isEmpty()) {
            return;
        }
        try {
            //循环菜单所有授权元素的KEY
            com.alibaba.fastjson2.JSONArray array = com.alibaba.fastjson2.JSON.parseArray(strElements);
            for (int i = 0; i < array.size(); i++) {
                String key = array.getString(i);
                if (StringUtils.isEmpty(key)) {
                    continue;
                }
                for (PageElement element : allAuths) {
                    //匹配到的权限，从原列表移除，这样就不会重复加入
                    if (menuId.equals(element.getMenuId()) && key.equals(element.getElementKey())) {
                        String mapKey=menuId + "-" + key;
                        curAuths.put(mapKey,element);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            log.warn("MenuService.collectElements解析JSON出错：" + strElements);
        }

    }

    /**
     * @param userId 用户ID
     * @param menu   租户菜单
     * @return java.util.List<java.util.HashMap>
     * @description: 查询用户的某个菜单页面权限
     * @author Elwin ZHANG
     * @date 2022/5/11 14:30
     */
    public List<PageElement> getUserPageAuth(String userId, TenantMenu menu) {
        String tenantId = ContextUtil.getCurrentTenantId();
        String menuId = menu.getPkId();
        QueryWrapper<PageElement> wrapper = new QueryWrapper<>();
        if (StringUtils.isEmpty(tenantId)) {
            wrapper.eq(COL_MENU_ID, menu.getMenuId());
        } else {
            wrapper.or(query -> query.eq(COL_MENU_ID, menu.getMenuId()));
            wrapper.or(query -> query.eq(COL_MENU_ID, menuId).eq(COL_TENANT_ID, tenantId));
        }
        IUser user = ContextUtil.getCurrentUser();
        //超级管理员或公司管理员拥有所有权限，非管理员则查询授权
        if (!user.isAdmin()) {
            List<GroupMenu> pageAuths = menuMapper.getUserPageAuth(userId, menuId);
            //没有授权，或菜单下没有控制到按钮
            if (pageAuths == null || pageAuths.size() == 0 || pageAuths.get(0).getHasElement() == 0) {
                return new ArrayList<>();
            }
            //合并所有授权的按钮或元素的KEY
            HashSet<String> setKeys = new HashSet<>();
            for (GroupMenu pageAuth : pageAuths) {
                String strElements = pageAuth.getElements();
                collectElements(setKeys, strElements);
            }
            //并没有授权相关的元素
            if (setKeys.size() == 0) {
                return new ArrayList<>();
            }
            //查询授权的元素信息
            wrapper.in("element_key", setKeys);
        }
        return pageElementMapper.selectList(wrapper);
    }

    /***
     * @description: 将JSON格式的页面元素KEY，加到集合中去重
     * @param setKeys 集合对象
     * @param strElements JSON格式的页面元素
     * @author Elwin ZHANG
     * @date 2022/6/21 10:24
     */
    private void collectElements(HashSet<String> setKeys, String strElements) {
        if (StringUtils.isEmpty(strElements) || "[]".equals(strElements)) {
            return;
        }
        try {
            JSONArray array = JSON.parseArray(strElements);
            for (int i = 0; i < array.size(); i++) {
                String key = array.getString(i);
                if (StringUtils.isNotEmpty(key)) {
                    setKeys.add(key);
                }
            }
        } catch (Exception e) {
            log.warn("MenuService.collectElements解析JSON出错：" + strElements);
        }
    }

    /***
     * @description: 查询菜单页面元素
     * @param menuId 菜单ID，如果有则只取当前菜单的页面元素
     * @return java.util.List<com.ezlcp.user.entity.PageElement>
     * @author Elwin ZHANG
     * @date 2022/5/11 10:52
     */
    public List<PageElement> getElements(String menuId) {
        QueryWrapper<PageElement> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(menuId)) {
            wrapper.eq(Constants.COL_MENU_ID, menuId);
        }
        return pageElementMapper.selectList(wrapper);
    }

    /***
     * @description 查询公司所有菜单元素
     * @param tenantId 公司ID
     * @return java.util.List<com.ezlcp.user.entity.PageElement>
     * @author Elwin ZHANG
     * @date 2023/3/28 18:00
     */
    public List<PageElement> getTenantElements(String tenantId) {
        QueryWrapper<PageElement> wrapper = new QueryWrapper<>();
        if (StringUtils.isEmpty(tenantId)) {
            wrapper.isNull(COL_TENANT_ID);
        } else {
            wrapper.or(query -> query.isNull(COL_TENANT_ID));
            wrapper.or(query -> query.eq(COL_TENANT_ID, tenantId));
        }
        return pageElementMapper.selectList(wrapper);
    }

    /***
     * @description: 获取某用户组拥有的所有菜单权限
     * @param groupId 用户组ID
     * @author Elwin ZHANG
     * @date 2022/5/11 11:10
     */
    public List<GroupMenu> getGroupMenus(String groupId) {
        QueryWrapper<GroupMenu> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(groupId)) {
            wrapper.eq(Constants.COL_GROUP_ID, groupId);
        }
        return groupMenuMapper.selectList(wrapper);
    }

    /***
     * @description: 修改用户组的菜单授权
     * @param  groupId 组IDID
     * @param array obj(menuId，elementId)
     * @author Elwin ZHANG
     * @date 2022/5/11 11:18
     */
    @Transactional(rollbackFor = Exception.class)
    public void editGroupMenus(String groupId, JSONArray array) {
        Group group = groupMapper.selectById(groupId);
        if (group == null) {
            return;
        }
        //取用户组关联的公司ID
        String tenantId = group.getTenantId();
        //删除当前用户组的旧的权限
        QueryWrapper<GroupMenu> wrapper = new QueryWrapper<>();
        wrapper.eq(Constants.COL_GROUP_ID, groupId);
        groupMenuMapper.delete(wrapper);
        //插入新的授权
        for (int i = 0; i < array.size(); i++) {
            JSONObject object = array.getJSONObject(i);
            String menuId;
            String elements = "[]";
            Integer hasElement;
            //解析传入参数
            try {
                menuId = object.getString("menuId");
                hasElement = object.getInteger("hasElement");
                Object objElements = object.getJSONArray("elements");
                if (objElements != null) {
                    elements = JSON.toJSONString(objElements);
                }
            } catch (Exception e) {
                log.error("editGroupMenus解析JSON出错：" + JSON.toJSONString(object));
                throw new RuntimeException("editGroupMenus:解析JSON出错");
            }
            if (StringUtils.isEmpty(menuId)) {
                throw new RuntimeException("editGroupMenus:菜單ID不能為空");
            }
            if (hasElement == null) {
                hasElement = 0;
            }
            GroupMenu groupMenu = new GroupMenu();
            groupMenu.setId(IdGenerator.getIdStr());
            groupMenu.setHasElement(hasElement.shortValue());
            groupMenu.setMenuId(menuId);
            groupMenu.setGroupId(groupId);
            groupMenu.setElements(elements);
            groupMenu.setTenantId(tenantId);
            groupMenuMapper.insert(groupMenu);
        }
    }



    /***
     * @description: 获取某用户组的授权信息，用于用户组授权页
     * @param groupId 用户组ID
     * @author Elwin ZHANG
     * @date 2023/3/28 11:47
     */
    public JsonResult getGroupAuthInfo(String groupId) {
        IUser user = ContextUtil.getCurrentUser();
        if (user == null) {
            return JsonResult.Fail("common.firstLogin");
        }
        String tenantId = user.getTenantId();
        //获取全部的菜单
        List<TenantMenu> lstMenus = null;
        if (user.isPlatformUser()) {
            lstMenus = getPlatformMenus();
        } else {
            lstMenus = getTenantAllMenus(tenantId);
        }
        if (lstMenus == null || lstMenus.size() == 0) {
            return JsonResult.Fail("common.getMenusFail");
        }
        //要返回的结果对象
        List<JSONObject> lstResult = new ArrayList<>();
        //用户组的菜单
        List<GroupMenu> groupMenus = getGroupMenus(groupId);
        //菜单的元素
        List<PageElement> elements = getTenantElements(tenantId);
        //循环处理，把实体对象转成JSON对象，方便增加新的属性
        for (var menu : lstMenus) {
            String menuId = menu.getId();
            String sourceId = menu.getMenuId();
            menu.setSourceId(sourceId);
            menu.setMenuId(menuId);
            //隐藏的子功能菜单忽略
            if (menu.getHidden() == 1 && menu.getIsDirect() == 0) {
                continue;
            }
            JSONObject objMenu = JSON.parseObject(JSON.toJSONString(menu));
            //菜单拥有的全部元素配置
            Object menuPageElements = elements.stream().filter(e ->
                    e.getMenuId().equals(menuId) || e.getMenuId().equals(sourceId)).collect(Collectors.toList());
            objMenu.put("pageElements", menuPageElements);
            //菜单当前授权的元素
            objMenu.put("checked", false);
            objMenu.put("curElements", new ArrayList<>());
            handleGroupMenuAuth(menuId, groupMenus, objMenu);
            lstResult.add(objMenu);
        }
        JsonResult result = JsonResult.Success("common.handleSuccess");
        result.setData(lstResult);
        result.setShow(false);
        return result;
    }

    /***
     * @description: 处理当前用户组菜单的授权
     * @param menuId 当前菜单ID
     * @param groupMenus 用户组的所有授权
     * @param objMenu 新的菜单对象
     * @author Elwin ZHANG
     * @date 2022/6/17 14:55
     */
    private void handleGroupMenuAuth(String menuId, List<GroupMenu> groupMenus, JSONObject objMenu) {
        if (groupMenus == null || groupMenus.size() == 0) {
            return;
        }
        for (GroupMenu groupMenu : groupMenus) {
            //找到当前菜单对应当前用户组的授权
            if (menuId.equals(groupMenu.getMenuId())) {
                objMenu.put("checked", true);
                String elements = groupMenu.getElements();
                if (StringUtils.isEmpty(elements) || "[]".equals(elements)) {
                    objMenu.put("curElements", new ArrayList<>());
                } else {
                    JSONArray array = JSON.parseArray(elements);
                    objMenu.put("curElements", array);
                }
                return;
            }
        }
    }

    /**
     * @param path 前端的路由
     * @return java.lang.String
     * @description: 根据前端的路由取对应的菜单名
     * @author Elwin ZHANG
     * @date 2022/6/14 18:22
     */
    public String getMenuName(String path) {
        if (StringUtils.isEmpty(path)) {
            return "";
        }
        String url = path;
        //取路径的最后一段
        if (path.contains("/")) {
            String[] arr = path.split("/");
            url = arr[arr.length - 1];
        }
        QueryWrapper<Menu> wrapper = new QueryWrapper<>();
        wrapper.eq("url", url);
        Menu menu = menuMapper.selectOne(wrapper);
        if (menu == null) {
            return "";
        }
        return menu.getMenuName();
    }

    @Override
    public BaseDao<Menu> getRepository() {
        return menuMapper;
    }
}